﻿//----------------------------------------------------------------
// Copyright (c) 2011-2013 
// All rights reserved.
// Author: Ben DaoWei 
// Contact: bendaowei@gmail.com
// Web Link: http://xnaeditor.codeplex.com
// ----------------------------------------------------------------

namespace UnityEngine.Components
{
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using UnityEngine.Core;
    using System.Xml;
    using UnityEngine.IO;
    using System.Xml.Serialization;

    public class Camera : UnityEngine.Core.Component
    {
        public readonly Vector3 DefaultLookAt = new Vector3(0, 0, 0);

        public readonly Vector3 DefaultPosition = new Vector3(0, 0, 500);

        public readonly Vector3 DefaultUp = new Vector3(0, 1, 0);

        private GraphicsDevice graphicsDevice = null;

        public override int Priority
        {
            get
            {
                return 1000000;
            }
        }

        public Camera()
            : this(null)
        {
        }

        public Camera(GameObject gameObject)
            : base(gameObject)
        {
            this.Position = this.DefaultPosition;
            this.LookAt = this.DefaultLookAt;
            this.Up = this.DefaultUp;
        }

        public float AspectRatio
        {
            set;
            get;
        }

        public BoundingFrustum BoundingFrustum
        {
            set;
            get;
        }

        public GraphicsDevice GraphcisDevice
        {
            set
            {
                this.AspectRatio = value.Viewport.AspectRatio;
                this.Projection = Matrix.CreatePerspectiveFieldOfView(this.FieldOfView, this.AspectRatio, this.NearClip, this.FarClip);
                this.View = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);
                this.BoundingFrustum = new BoundingFrustum(View * Projection);
                graphicsDevice = value;
            }
            get
            {
                return graphicsDevice;
            }
        }

        private float _FieldOfView = MathHelper.ToRadians(45);
        public float FieldOfView
        {
            get
            {
                return _FieldOfView;
            }
            set
            {
                _FieldOfView = value;
            }
        }
        private float _FarClip = 30000.0f;
        public float FarClip
        {
            get
            {
                return _FarClip;
            }
            set
            {
                _FarClip = value;
            }
        }

        private float _NearClip = 1.0f;
        public float NearClip
        {
            get
            {
                return _NearClip;
            }
            set
            {
                _NearClip = value;
            }
        }

        [Editable]
        public Vector3 Position
        {
            set;
            get;
        }

        [Editable]
        public Vector3 LookAt
        {
            set;
            get;
        }

        [Editable]
        public Vector3 Up
        {
            get;
            set;
        }

        public Matrix Projection
        {
            get;
            set;
        }

        public Matrix View
        {
            get;
            set;
        }

        public Vector2 GetScreenPosisionFromWorld(Vector3 position, Matrix world)
        {
            Vector3 p = this.GraphcisDevice.Viewport.Project(position, this.Projection, this.View, world);
            return new Vector2(p.X, p.Y);
        }

        public Ray GetRayFromScreen(int x, int y)
        {
            if (x < 0) x = 0;
            if (x > this.GraphcisDevice.Viewport.Width) x = this.GraphcisDevice.Viewport.Width;
            if (y < 0) y = 0;
            if (y > this.GraphcisDevice.Viewport.Height) y = this.GraphcisDevice.Viewport.Height;

            Matrix world = Matrix.Identity;
            Vector2 screenPosition = new Vector2(x, y);

            Vector3 nearSource = new Vector3(screenPosition, 0f);
            Vector3 farSource = new Vector3(screenPosition, 1f);
            Vector3 nearPoint = this.GraphcisDevice.Viewport.Unproject(nearSource, this.Projection, this.View, world);
            Vector3 farPoint = this.GraphcisDevice.Viewport.Unproject(farSource, this.Projection, this.View, world);

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            // and then create a new ray using nearPoint as the source.
            return new Ray(nearPoint, direction);
        }

        public override void Startup(GameTime gameTime)
        {
            this.GraphcisDevice = this.GameObject.GameScene.Game.GraphicsDevice;
            base.Startup(gameTime);
        }

        public override void Update(GameTime gameTime)
        {
            View = Matrix.CreateLookAt(this.Position, this.LookAt, this.Up);
            this.BoundingFrustum = new BoundingFrustum(View * Projection);
        }
    }
}
